package ht

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// SetUrlLog 设置请求链接打印日志
func (c *HttpClient) SetUrlLog(fx func(a ...any)) {
	c.printLog = fx
}

// SetProxy 设置代理
func (c *HttpClient) SetProxy(proxyRaw string) error {
	_, err := NewProxy(proxyRaw)
	if err != nil {
		return err
	}
	c.proxyRaw = proxyRaw
	return nil
}

// SetTimeout 设置超时时间
func (c *HttpClient) SetTimeout(timeoutSecond uint) {
	if timeoutSecond <= 0 {
		return
	} else {
		c.timeout = time.Duration(timeoutSecond) * time.Second
	}
}

// SetUserAgent 设置UA信息
func (c *HttpClient) SetUserAgent(ua string) {
	c.ua = UserAgent(ua)
}

// Proxy 设置临时代理
// proxyRaw 代理信息，空值则不使用代理；非空："socks5://127.0.0.1:1080" "socks5://admin:password@192.168.1.2:8888"
func (c *HttpClient) Proxy(proxyRaw string) *HttpClient {
	return &HttpClient{
		proxyRaw: proxyRaw,
		timeout:  c.timeout,
		ua:       c.ua,
	}
}

// Timeout 设置临时超时时间
func (c *HttpClient) Timeout(timeoutSecond uint) *HttpClient {
	if timeoutSecond <= 0 {
		return &HttpClient{
			proxyRaw: c.proxyRaw,
			timeout:  0,
			ua:       c.ua,
		}
	}
	return &HttpClient{
		proxyRaw: c.proxyRaw,
		timeout:  time.Duration(timeoutSecond) * time.Second,
		ua:       c.ua,
	}
}

// UA 设置临时User-Agent
func (c *HttpClient) UA(ua string) *HttpClient {
	return &HttpClient{
		proxyRaw: c.proxyRaw,
		timeout:  c.timeout,
		ua:       UserAgent(ua),
	}
}

// HttpClient 返回一个http客户端
func (c *HttpClient) HttpClient() *http.Client {
	pxy, _ := NewProxy(c.proxyRaw)
	return &http.Client{
		Transport: pxy,
		Timeout:   c.timeout,
	}
}

// GET 发送GET表单请求
func (c *HttpClient) GET(urlRaw string, params RequestParams) (*http.Response, error) {
	return c.form(GET, urlRaw, params, "")
}

// POST 发送POST表单请求
func (c *HttpClient) POST(urlRaw string, params RequestParams) (*http.Response, error) {
	return c.form(POST, urlRaw, params, "application/x-www-form-urlencoded")
}

// DELETE 发送DELETE表单请求
func (c *HttpClient) DELETE(urlRaw string, params RequestParams) (*http.Response, error) {
	return c.form(DELETE, urlRaw, params, "")
}

// PUT 发送PUT表单请求
func (c *HttpClient) PUT(urlRaw string, params RequestParams) (*http.Response, error) {
	return c.form(PUT, urlRaw, params, "application/x-www-form-urlencoded")
}

// PostJsonMap 发送POST请求，请求体为jsonData和params.Body合并的json编码结果
func (c *HttpClient) PostJsonMap(urlRaw string, params RequestParams, jsonData map[string]any) (*http.Response, error) {
	for k, v := range jsonData {
		params.Body[k] = v
	}
	return c.formJson(POST, urlRaw, params)
}

// PutJsonMap 发送PUT请求，请求体为jsonData和params.Body合并的json编码结果
func (c *HttpClient) PutJsonMap(urlRaw string, params RequestParams, jsonData map[string]any) (*http.Response, error) {
	for k, v := range jsonData {
		params.Body[k] = v
	}
	return c.formJson(PUT, urlRaw, params)
}

// PostJsonStruct 发送POST请求，请求体为structPointer和params.Body合并的json编码结果
func (c *HttpClient) PostJsonStruct(urlRaw string, params RequestParams, structPointer any) (*http.Response, error) {
	marshal, err := json.Marshal(structPointer)
	if err == nil {
		var tempMap = make(map[string]any)
		err = json.Unmarshal(marshal, &tempMap)
		if err == nil {
			for k, v := range tempMap {
				params.Body[k] = v
			}
		}
	}
	return c.formJson(POST, urlRaw, params)
}

// PutJsonStruct 发送PUT请求，请求体为structPointer和params.Body合并的json编码结果
func (c *HttpClient) PutJsonStruct(urlRaw string, params RequestParams, structPointer any) (*http.Response, error) {
	marshal, err := json.Marshal(structPointer)
	if err == nil {
		var tempMap = make(map[string]any)
		err = json.Unmarshal(marshal, &tempMap)
		if err == nil {
			for k, v := range tempMap {
				params.Body[k] = v
			}
		}
	}
	return c.formJson(PUT, urlRaw, params)
}

// PostBody 发送POST请求，请求体为body
func (c *HttpClient) PostBody(urlRaw string, params RequestParams, body []byte, contentType string) (*http.Response, error) {
	return c.formBody(POST, urlRaw, params, body, contentType)
}

// PutBody 发送PUT请求，请求体为body
func (c *HttpClient) PutBody(urlRaw string, params RequestParams, body []byte, contentType string) (*http.Response, error) {
	return c.formBody(PUT, urlRaw, params, body, contentType)
}

func (c *HttpClient) form(method Method, urlRaw string, params RequestParams, contentType string) (*http.Response, error) {
	query := url.Values{}
	for k, v := range params.Query {
		query.Set(k, v)
	}
	body := url.Values{}
	for k, v := range params.Body {
		body.Set(k, fmt.Sprint(v))
	}
	var req *http.Request
	var err error

	// 拼接请求链接
	if len(params.Query) > 0 {
		if strings.HasSuffix(urlRaw, "?") {
			urlRaw = urlRaw[:len(urlRaw)-1]
		}
		if strings.Contains(urlRaw, "?") {
			urlRaw += "&" + query.Encode()
		} else {
			urlRaw += "?" + query.Encode()
		}
	}

	if len(params.Body) <= 0 {
		req, err = http.NewRequest(fmt.Sprint(method), urlRaw, nil)
	} else {
		req, err = http.NewRequest(fmt.Sprint(method), urlRaw, strings.NewReader(body.Encode()))
	}
	if err != nil {
		return nil, err
	}
	for k, v := range params.Header {
		req.Header.Set(k, v)
	}
	if contentType != "" {
		req.Header.Set("Content-Type", contentType)
	}
	pxy, _ := NewProxy(c.proxyRaw)
	client := &http.Client{
		Transport: pxy,
		Timeout:   c.timeout,
	}
	if req.Header.Get("Accept") == "" {
		req.Header.Set("Accept", `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9`)
	}
	if req.Header.Get("Connection") == "" {
		req.Header.Set("Connection", "close")
	}
	req.Header.Set("User-Agent", fmt.Sprint(c.ua))
	if c.printLog != nil {
		c.printLog(urlRaw)
	}
	do, err := client.Do(req)
	return do, err
}

func (c *HttpClient) formJson(method Method, urlRaw string, params RequestParams) (*http.Response, error) {
	query := url.Values{}
	for k, v := range params.Query {
		query.Set(k, v)
	}
	var req *http.Request
	var err error

	// 拼接请求链接
	if len(params.Query) > 0 {
		if strings.HasSuffix(urlRaw, "?") {
			urlRaw = urlRaw[:len(urlRaw)-1]
		}
		if strings.Contains(urlRaw, "?") {
			urlRaw += "&" + query.Encode()
		} else {
			urlRaw += "?" + query.Encode()
		}
	}

	if len(params.Body) <= 0 {
		req, err = http.NewRequest(fmt.Sprint(method), urlRaw, nil)
	} else {
		marshal, _ := json.Marshal(&params.Body)
		req, err = http.NewRequest(fmt.Sprint(method), urlRaw, bytes.NewReader(marshal))
	}
	if err != nil {
		return nil, err
	}
	for k, v := range params.Header {
		req.Header.Set(k, v)
	}
	req.Header.Set(ContentType, "application/json;charset=UTF-8")
	pxy, _ := NewProxy(c.proxyRaw)
	client := &http.Client{
		Transport: pxy,
		Timeout:   c.timeout,
	}
	if req.Header.Get("Accept") == "" {
		req.Header.Set("Accept", `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9`)
	}
	if req.Header.Get("Connection") == "" {
		req.Header.Set("Connection", "close")
	}
	req.Header.Set("User-Agent", fmt.Sprint(c.ua))
	if c.printLog != nil {
		c.printLog(urlRaw)
	}
	do, err := client.Do(req)
	return do, err
}

func (c *HttpClient) formBody(method Method, urlRaw string, params RequestParams, body []byte, contentType string) (*http.Response, error) {
	query := url.Values{}
	for k, v := range params.Query {
		query.Set(k, v)
	}
	var req *http.Request
	var err error

	// 拼接请求链接
	if len(params.Query) > 0 {
		if strings.HasSuffix(urlRaw, "?") {
			urlRaw = urlRaw[:len(urlRaw)-1]
		}
		if strings.Contains(urlRaw, "?") {
			urlRaw += "&" + query.Encode()
		} else {
			urlRaw += "?" + query.Encode()
		}
	}

	req, err = http.NewRequest(fmt.Sprint(method), urlRaw, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	for k, v := range params.Header {
		req.Header.Set(k, v)
	}
	if contentType != "" {
		req.Header.Set(ContentType, contentType)
	}
	pxy, _ := NewProxy(c.proxyRaw)
	client := &http.Client{
		Transport: pxy,
		Timeout:   c.timeout,
	}
	if req.Header.Get("Accept") == "" {
		req.Header.Set("Accept", `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9`)
	}
	if req.Header.Get("Connection") == "" {
		req.Header.Set("Connection", "close")
	}
	req.Header.Set("User-Agent", fmt.Sprint(c.ua))
	if c.printLog != nil {
		c.printLog(urlRaw)
	}
	do, err := client.Do(req)
	return do, err
}
